from fastapi import APIRouter , Depends, HTTPException, status, Path
from pydantic import BaseModel
from models import User
from passlib.context import CryptContext
from database import sessionmaker
from typing import Annotated
from sqlalchemy.orm import Session
from fastapi.security import OAuth2PasswordRequestForm , OAuth2PasswordBearer
from jose import JWTError, jwt
from datetime import datetime, timedelta

SessionLocal = sessionmaker

# 创建API路由器实例，设置路由前缀为"/auth"，标签为"auth"，并定义404响应
router = APIRouter(
    prefix="/auth",
    tags=["auth"],
    responses={404: {"description": "Not found"}}
) 

# 定义用于JWT加密的密钥
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
# 指定JWT加密算法
ALGORITHM = "HS256"

# 创建密码加密上下文，使用bcrypt算法
bcypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# 创建OAuth2密码Bearer令牌，指定令牌URL
oauth2_bearer = OAuth2PasswordBearer(tokenUrl="auth/token")

# 定义获取数据库会话的依赖函数
def get_db():
    # 创建新的数据库会话
    db = SessionLocal()
    try:
        # 使用yield返回数据库会话
        yield db
    finally:
        # 确保在请求结束时关闭数据库会话
        db.close()

# 创建类型注解的数据库依赖项，用于FastAPI的依赖注入系统
db_dependency = Annotated[Session, Depends(get_db)]

# 验证用户身份的函数
def authenticate_user(username: str, password: str, db):
    user = db.query(User).filter(User.username == username).first()
    if not user:
        return False
    # 验证密码是否匹配，如果不匹配返回False
    if not bcypt_context.verify(password, user.hashed_password):
        return False
    # 验证通过，返回用户对象
    return user

# 创建访问令牌的函数
def create_access_token(username: str, user_id: int,role: str , expires_delta: timedelta):
    # 创建包含用户信息的编码字典
    encode = {"sub": username, "id": user_id, "role": role}
    # 计算令牌过期时间
    expires = datetime.now() + expires_delta
    # 将过期时间添加到编码字典中
    encode.update({"exp": expires})
    # 使用JWT加密算法生成令牌并返回
    return jwt.encode(encode, SECRET_KEY, algorithm=ALGORITHM)

# 获取当前用户的异步函数
async def get_current_user(token: Annotated[str, Depends(oauth2_bearer)]):
    try:
        # 解码JWT令牌
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        # 从载荷中获取用户信息
        username: str = payload.get("sub")
        user_id: int = payload.get("id")
        user_role: str = payload.get("role")
        # 如果用户名或ID为空，抛出未授权异常
        if username is None or user_id is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,   
                detail="Could not validate user",
                headers={"WWW-Authenticate": "Bearer"}, 
            )
        # 返回包含用户信息的字典
        return {"username": username, "id": user_id , "role": user_role}
    except JWTError:
        # 如果JWT解码失败，抛出未授权异常
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Could not validate user",
            headers={"WWW-Authenticate": "Bearer"},
        )


class CreatUserRequest(BaseModel):
    username: str
    email: str
    first_name: str
    last_name: str
    password: str
    role: str

class Token(BaseModel):
    access_token: str
    token_type: str


@router.post("/", status_code=status.HTTP_201_CREATED)
async def create_user(db :db_dependency ,
                        create_user_request: CreatUserRequest):
    create_user_model = User(
        email = create_user_request.email,
        username = create_user_request.username,
        first_name = create_user_request.first_name,
        last_name = create_user_request.last_name,
        hashed_password = bcypt_context.hash(create_user_request.password),
        role = create_user_request.role ,
        is_active = True
    )
    db.add(create_user_model)
    db.commit()

@router.post("/token", response_model=Token)
async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm , Depends()], 
                                db: Session = Depends(get_db)):
    user = authenticate_user(form_data.username, form_data.password, db)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    token = create_access_token( user.username, user.id, user.role , expires_delta=timedelta(minutes=20))
    return {"access_token": token, "token_type": "bearer"}

